﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx.WoWInternals.WoWObjects;
using Styx;
using TreeSharp;
using Styx.WoWInternals;

namespace FelMaster
{
    public class HealerBase
    {
        private LocalPlayer Me { get { return StyxWoW.Me; } }

        private static HealerBase instance = new HealerBase();
        public static HealerBase Instance
        {
            get
            {
                return instance;
            }
        }

        private HealerBase()
        {
        }

        private void Log(string s, params object[] a)
        {
            SimCraftBase.Instance.Log(s, a);
        }

        public enum TargetFilter
        {
            None = 0,
            Tanks = 1,
            Healers = 2,
            DPS = 4
        }

        private List<WoWPlayer> raidUnitsFilter(TargetFilter filter)
        {
            var infos = Me.RaidMemberInfos;

            var raid = new List<WoWPlayer>();
            if (Me.IsInRaid)
                raid = Me.RaidMembers;
            else if (Me.IsInParty)
                raid = Me.PartyMembers;
            else
                raid.Add(Me);

            if (filter == TargetFilter.None)
                return raid;
            else if (filter == TargetFilter.Tanks)
            {
                var tanks = infos.Where(x => x.Role == WoWPartyMember.GroupRole.Tank);
                return raid.Where(x => tanks.Any(y => y.Guid == x.Guid)).ToList();
            }
            else if (filter == TargetFilter.Healers)
            {
                var healers = infos.Where(x => x.Role == WoWPartyMember.GroupRole.Healer);
                return raid.Where(x => healers.Any(y => y.Guid == x.Guid)).ToList();
            }
            else if (filter == TargetFilter.DPS)
            {
                var tanks = infos.Where(x => x.Role == WoWPartyMember.GroupRole.Tank);
                var healers = infos.Where(x => x.Role == WoWPartyMember.GroupRole.Healer);
                return raid.Where(x => !tanks.Any(y => y.Guid == x.Guid) && !healers.Any(y => y.Guid == x.Guid)).ToList();
            }
            else
                return null;
        }

        public delegate bool RaidFilterDelegate(WoWPlayer unit);
        private Composite FindRaidTarget(CanRunDecoratorDelegate cond, TargetFilter filter, RaidFilterDelegate raidFilter, Comparison<WoWPlayer> compare, string label, params Composite[] children)
        {
            return new Decorator(cond,
                new Sequence(
                    //get a target
                    new TreeSharp.Action(delegate(object a)
                    {
                        var raid = raidUnitsFilter(filter).Where(x =>
                            ObjectManager.ObjectList.Any(y => y.Guid == x.Guid) &&
                            raidFilter(x.ToPlayer()) &&
                            x.Distance2DSqr < 40 * 40).ToList();
                        raid.Sort(compare);
                        var target = raid.FirstOrDefault();
                        if (target != null)
                        {
                            Log(label, target.ToPlayer().Name);
                            target.ToPlayer().Target();
                            return RunStatus.Success;
                        }
                        else
                            return RunStatus.Failure;
                    }),

                    //if success, keep going. Else quit sub routine
                    new PrioritySelector(children)
                )
            );
        }
        public Composite FindTank(CanRunDecoratorDelegate cond, RaidFilterDelegate filter, Comparison<WoWPlayer> compare, params Composite[] children)
        {
            return FindRaidTarget(cond, TargetFilter.Tanks, filter, compare, "On tank {0}:", children);
        }
        public Composite FindHealer(CanRunDecoratorDelegate cond, RaidFilterDelegate filter, Comparison<WoWPlayer> compare, params Composite[] children)
        {
            return FindRaidTarget(cond, TargetFilter.Healers, filter, compare, "On healer {0}:", children);
        }
        public Composite FindDPS(CanRunDecoratorDelegate cond, RaidFilterDelegate filter, Comparison<WoWPlayer> compare, params Composite[] children)
        {
            return FindRaidTarget(cond, TargetFilter.DPS, filter, compare, "On dps {0}:", children);
        }
        public Composite FindRaidMember(CanRunDecoratorDelegate cond, RaidFilterDelegate filter, Comparison<WoWPlayer> compare, params Composite[] children)
        {
            return FindRaidTarget(cond, TargetFilter.None, filter, compare, "On raid member {0}:", children);
        }

        private Composite FindParty_subroutine(int partyIndex, int minAverageHealth, int maxAverageHealth, float maxDistanceBetweenPlayers, int minUnits, string label)
        {
            return new TreeSharp.Action(a =>
            {
                var players = Me.RaidMemberInfos.Where(x =>
                    x.GroupNumber == partyIndex &&
                    ObjectManager.ObjectList.Any(y => y.Guid == x.Guid)
                    && x.Location3D.Distance2DSqr(Me.Location) < 40 * 40);
                WoWPlayer best = null;
                int score = minUnits - 1;
                foreach (var player in players)
                {
                    var hits = players.Where(p => p.Location3D.Distance2DSqr(player.Location3D) < maxDistanceBetweenPlayers);
                    var avgHealth = hits.Average(p => p.Health * 100 / p.HealthMax);
                    var count = hits.Count();
                    if (avgHealth >= minAverageHealth && avgHealth < maxAverageHealth && count > score)
                    {
                        best = player.ToPlayer();
                        score = count;
                    }
                }

                if (best != null)
                {
                    Log(label, best.Name);
                    best.Target();
                    return RunStatus.Success;
                }
                else
                    return RunStatus.Failure;
            });
        }

        public Composite FindParty(CanRunDecoratorDelegate cond, int minAverageHealth, int maxAverageHealth, float maxDistanceBetweenPlayers, int minUnits, params Composite[] children)
        {
            return new Decorator(cond,
                new Sequence(
                    new PrioritySelector(
                        FindParty_subroutine(0, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 1 member: {0}"),
                        FindParty_subroutine(1, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 2 member: {0}"),
                        FindParty_subroutine(2, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 3 member: {0}"),
                        FindParty_subroutine(3, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 4 member: {0}"),
                        FindParty_subroutine(4, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 5 member: {0}"),
                        FindParty_subroutine(5, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 6 member: {0}"),
                        FindParty_subroutine(6, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 7 member: {0}"),
                        FindParty_subroutine(7, minAverageHealth, maxAverageHealth, maxDistanceBetweenPlayers, minUnits, "Target party 8 member: {0}")
                    ),
                    new PrioritySelector(children)
                )
            );
        }

        public Composite HealMe(string name, CanRunDecoratorDelegate cond, string label)
        {
            return new Decorator(x => { return cond(x) && SimCraftBase.Instance.CanCast(name); },
                new Sequence(
                    new TreeSharp.Action(a => Me.Target()),
                    SimCraftBase.Instance.CastSpell(name, a => true, label)
                )
            );
        }
    }
}
